#include "objsToKam1.h"
struct Animation
{
	string name;
	vector<int> keyframes;
	vector<int> keyframes_map;
	vector<INTERPOLATION_FUNC> inter_func;
};
void objsToKam1(char* objList,char *out,char *animationList,char* option)
{
	char line[200];
	ifstream fin[100]; // maximum key number is 100
	arrayVec3 vertices[100];
	arrayVec2 texcoords[100];
	arrayVec3 normals[100];
	arrayVec3 faces[100];
	vector<Animation> animations;
	int isStaticNormal = 0;
	int isStaticPosition = 0;
	int isStaticTexcoord = 1;
	QUALITY q = QUALITY_LOW;

	vector<string> fileList;
	split(string(objList),fileList,';');
	int keyNum = fileList.size();
	for(int i=0;i<keyNum;i++)
	{
		fin[i].open( fileList[i].c_str());
		if(!fin[i].good() )
		{
			cout<<"Error when open file :"<<fileList[i];
			return;
		}
	}

	// Parse animation list
	vector<string> ani_list;
	split(string(animationList),ani_list,';');
	for(int i=0;i<ani_list.size();i++)
	{
		vector<string> ani_atts;
		split(ani_list[i],ani_atts,':');
		if(ani_atts.size()!=3&& ani_atts.size()!=4)
		{
			cout<<"Error when parse animation list:"<<ani_list[i];
			return;
		}
		else
		{
			Animation temp;
			temp.name.append(ani_atts[0]);
			vector<string> keys;
			vector<string> keys_map;
			split(ani_atts[1],keys,',');
			split(ani_atts[2],keys_map,',');
			if( keys.size()!=keys_map.size())
			{
				cout<<"Error when parse animation list:"<<ani_list[i];
				return;
			}
			for(int j=0;j<keys.size();j++)
			{
				temp.keyframes.push_back( stoi(keys[j]));
				temp.keyframes_map.push_back( stoi(keys_map[j]));
			}
			if( ani_atts.size()==4)
			{
				vector<string> inter_func_str;
				split(ani_atts[3],inter_func_str,',');
				if( inter_func_str.size()!= (keys.size()-1))
				{
					cout<<"Error when parse animation list:"<<ani_list[i]<<" (inter_func num != key num -1 )";
				}
				for(int j=0;j<inter_func_str.size();j++)
				{
					temp.inter_func.push_back( (INTERPOLATION_FUNC)(stoi(inter_func_str[j])));
				}
			}
			else
			{
				temp.inter_func.resize( keys.size()-1,IF_LINEAR);
			}
			animations.push_back(temp);
		}
	}
	// Parser option
	if(option)
	{
		vector<string> ops;
		split(string(option),ops,';');
		for(int i=0;i<ops.size();i++)
		{
			vector<string> atts;
			split(ops[i],atts,':');
			if( !atts[0].compare("static_component"))
			{
				isStaticTexcoord = 0;
				vector<string> comp;
				split(atts[1],comp,',');
				for(int j=0;j< comp.size();j++)
				{
					if( !comp[j].compare("position"))
						isStaticPosition = 1;
					else if( !comp[j].compare("normal"))
						isStaticNormal = 1;
					else if( !comp[j].compare("texcoord"))
						isStaticTexcoord = 1;
				}
			}
			else if( !atts[0].compare("hq"))
			{
				q = QUALITY_HIGH;
			}
			else
			{
				cout<<"Error: Invalid option :"<<atts[0];
				return;
			}
		}
	}
	cout<<"\nStart read obj files..";
	for(int key=0;key<keyNum;key++)
	{
		while(!fin[key].eof())
		{
			vector<string> atts;
			fin[key].getline(line,199);
			split(string(line), atts,' ');
			if( !atts[0].compare("v") ) // vertices
			{
				vertices[key].push_back( gewVec3( stof(atts[1]),stof(atts[2]),stof(atts[3])));
			}
			if( !atts[0].compare("vn") ) // normals
			{
				gewVec3 raw_normal(stof(atts[1]),stof(atts[2]),stof(atts[3]));
				raw_normal.normalize();
				normals[key].push_back(raw_normal);
			}
			if( !atts[0].compare("vt") ) // texcoords
			{
				texcoords[key].push_back( gewVec2( stof(atts[1]),stof(atts[2])));
			}
			if( !atts[0].compare("f") ) // face, auto triangulate the face as triangle fan
			{
				gewVec3 pre;
				vector<string> face_atts;
				for (int i=1; i<atts.size(); i++) 
				{
					if (i > 3)
					{
						face_atts.clear();
						split(atts[1],face_atts,'/');
						gewVec3 fan_center(stoi(face_atts[0]),stoi(face_atts[1]),stoi(face_atts[2]));
						faces[key].push_back(fan_center);
						faces[key].push_back(pre);
					}
					face_atts.clear();
					split(atts[i],face_atts,'/');
					pre = gewVec3( stoi(face_atts[0]),stoi(face_atts[1]),stoi(face_atts[2]));
					faces[key].push_back(pre);
				}
			}
		}
	}
	for(int i=0;i<keyNum;i++)
		fin[i].close();

	// Check vertices, normals, faces list of each frame to dectect error
	int vertex_num = vertices[0].size();
	int normal_num = normals[0].size();
	int texcoord_num = texcoords[0].size();
	int face_num = faces[0].size();
	for(int i=1;i<keyNum;i++)
	{
		int my_vertex_num = vertices[i].size();
		int my_normal_num = normals[i].size();
		int my_texcoord_num = texcoords[i].size();
		int my_face_num = faces[i].size();
		if( my_vertex_num!=vertex_num)
		{
			cout<<"Error: vertices number in file "<<fileList[i]<<" is diffirent with file "<<fileList[0];
			return;
		}
		if( my_normal_num!=normal_num)
		{
			cout<<"Error: normals number in file "<<fileList[i]<<" is diffirent with file "<<fileList[0];
			return;
		}
		if( my_texcoord_num!=texcoord_num)
		{
			cout<<"Error: texcoords number in file "<<fileList[i]<<" is diffirent with file "<<fileList[0];
			return;
		}
		if( my_face_num!= face_num)
		{
			cout<<"Error: faces number in file "<<fileList[i]<<" is diffirent with file "<<fileList[0];
			return;
		}
		for( int j=0;j<face_num;j++)
		{
			if( !(faces[i][j]==faces[0][j]))
			{
				cout<<"Error: faces in file "<<fileList[i]<<" is diffirent with file "<<fileList[0];
				return;
			}
		}
	}

	//Calculate bounding volume
	gewAabb boundingBox;
	gewSphere boundingSphere;

	boundingBox.minVertex = vertices[0][0];
	boundingBox.maxVertex = vertices[0][0];
	
	for(int k=0;k<keyNum;k++)
	for(int i = 0; i < vertices[k].size();i++)
	{
		boundingBox.addInternalPoint(vertices[k][i]);
	}
	arrayVec3 totalVertices;
	for(int k=0;k<keyNum;k++)
	for(int i = 0; i < vertices[k].size();i++)
	{
		totalVertices.push_back(vertices[k][i]);
	}
	//Calculate bounding sphere 
	boundingSphere.calculate(totalVertices);

	arrayVec3 overtices;
	arrayVec2 otexcoords;
	arrayVec3 onormals;
	vector<unsigned int> oindices;
	arrayVec3 processed;
	cout<<"\nStart convert mesh data..";
	for(int i=0;i< faces[0].size();i++)
	{
		int offset = isIn(faces[0][i],processed);
		if(offset==-1) // new
		{
			processed.push_back( faces[0][i]);
			if(isStaticPosition)
				overtices.push_back( vertices[0][ faces[0][i].x-1]);
			else
			{
				for(int key=0;key<keyNum;key++)
					overtices.push_back( vertices[key][ faces[0][i].x-1]);
			}
			if(isStaticTexcoord)
				otexcoords.push_back( texcoords[0][ faces[0][i].y-1]);
			else
			{
				for(int key=0;key<keyNum;key++)
					otexcoords.push_back( texcoords[key][ faces[0][i].y-1]);
			}
			if(isStaticNormal)
				onormals.push_back( normals[0][ faces[0][i].z-1]);
			else
			{
				for(int key=0;key<keyNum;key++)
					onormals.push_back( normals[key][ faces[0][i].z-1]);
			}
			oindices.push_back( processed.size()-1);
		}
		else
		{
			oindices.push_back( offset);
		}
	}
	
	cout<<"\nStart write output file..";
	FIXED_POINT_FORMAT ver_fp_format = findFixedPointFormat(overtices);
	FIXED_POINT_FORMAT tex_fp_format = findFixedPointFormat(otexcoords);
	FIXED_POINT_FORMAT nor_fp_format = findFixedPointFormat(onormals);
	if(q==QUALITY_HIGH)
	{
		ver_fp_format= FP_1_15_16;
	}
	cout<<"\nVertices Fixed point format:"<<getFixedPointStr(ver_fp_format);
	cout<<"\nTexcoords Fixed point format:"<<getFixedPointStr(tex_fp_format);
	cout<<"\nNormals Fixed point format:"<<getFixedPointStr(nor_fp_format);
	cout<<"\nVertices number (per keyframe):"<<overtices.size()/keyNum;
	cout<<"\nIndices number:"<<oindices.size();

	ofstream fout(out,ios_base::out|ios_base::binary| ios_base::trunc);
	unsigned int temp;
	// magic number
	fout<<"kam1";
	// type of this mesh : 
	//	1 : mesh that contain 3-coord per position, 2-coord per texcoord, 3-coord per normal
	fout<<char(1);
	// Vertices Fixed point format
	temp = ver_fp_format;
	fout.write((char*)(&temp),4);
	// Texcoords Fixed point format
	temp = tex_fp_format;
	fout.write((char*)(&temp),4);
	// Normals Fixed point format
	temp = nor_fp_format;
	fout.write((char*)(&temp),4);
	// vertices count
	temp = overtices.size()/keyNum;
	fout.write((char*)(&temp),4);

	// Animation number
	temp = animations.size();
	fout.write((char*)(&temp),4);

	// Keyframe number
	temp = keyNum;
	fout.write((char*)(&temp),4);

	// options flag
	temp = (isStaticPosition<<2)+ (isStaticTexcoord<<1)+isStaticNormal ;
	fout.write((char*)(&temp),4);

	// LOD level number
	fout<<char(1);
	// indices length of LOD lv0
	temp = oindices.size();
	fout.write((char*)(&temp),4);


	fout.seekp(96,ios_base::beg);
	// writing vertices attribute 
	int byte_per_ver_coord = getFixedPointSize(ver_fp_format);
	int byte_per_tex_coord = getFixedPointSize(tex_fp_format);
	int byte_per_nor_coord = getFixedPointSize(nor_fp_format);
	for(int i=0;i<overtices.size();i++)
	{
		// position
		temp = floatToFixed( overtices[i].x, ver_fp_format);
		fout.write((char*)(&temp),byte_per_ver_coord);
		temp = floatToFixed( overtices[i].y,ver_fp_format);
		fout.write((char*)(&temp),byte_per_ver_coord);
		temp = floatToFixed( overtices[i].z,ver_fp_format);
		fout.write((char*)(&temp),byte_per_ver_coord);	
	}
	for(int i=0;i<otexcoords.size();i++)
	{
		// texcoord
		temp = floatToFixed( otexcoords[i].x,tex_fp_format);
		fout.write((char*)(&temp),byte_per_tex_coord);
		temp = floatToFixed( otexcoords[i].y,tex_fp_format);
		fout.write((char*)(&temp),byte_per_tex_coord);
	}
	for(int i=0;i<onormals.size();i++)
	{
		// normals
		temp = floatToFixed( onormals[i].x,nor_fp_format);
		fout.write((char*)(&temp),byte_per_nor_coord);
		temp = floatToFixed( onormals[i].y,nor_fp_format);
		fout.write((char*)(&temp),byte_per_nor_coord);
		temp = floatToFixed( onormals[i].z,nor_fp_format);
		fout.write((char*)(&temp),byte_per_nor_coord);		
	}
	// writing indices
	for(int i=0;i<oindices.size();i++)
	{
		temp = oindices[i];
		fout.write((char*)(&temp),2);
	}
	// writing animation
	for(int i=0;i< animations.size();i++)
	{
		int key_num = animations[i].keyframes.size();
		temp = key_num;
		fout.write((char*)(&temp),2);
		animations[i].name.resize(20,' ');
		fout<<animations[i].name;	
		for(int j=0;j< key_num;j++)
		{
			temp = animations[i].keyframes[j];
			fout.write((char*)(&temp),2);
		}
		for(int j=0;j< key_num;j++)
		{
			temp = animations[i].keyframes_map[j];
			fout.write((char*)(&temp),2);
		}
		for(int j=0;j< (key_num-1);j++)
		{
			temp = animations[i].inter_func[j];
			fout.write((char*)(&temp),2);
		}
	}

	// writing bounding volume
	// aabb
	temp = floatToFixed( boundingBox.minVertex.x,ver_fp_format);
	fout.write((char*)(&temp),byte_per_ver_coord);
	temp = floatToFixed( boundingBox.minVertex.y,ver_fp_format);
	fout.write((char*)(&temp),byte_per_ver_coord);
	temp = floatToFixed( boundingBox.minVertex.z,ver_fp_format);
	fout.write((char*)(&temp),byte_per_ver_coord);
	temp = floatToFixed( boundingBox.maxVertex.x,ver_fp_format);
	fout.write((char*)(&temp),byte_per_ver_coord);
	temp = floatToFixed( boundingBox.maxVertex.y,ver_fp_format);
	fout.write((char*)(&temp),byte_per_ver_coord);
	temp = floatToFixed( boundingBox.maxVertex.z,ver_fp_format);
	fout.write((char*)(&temp),byte_per_ver_coord);
	// bounding sphere
	temp = floatToFixed( boundingSphere.center.x,ver_fp_format);
	fout.write((char*)(&temp),byte_per_ver_coord);
	temp = floatToFixed( boundingSphere.center.y,ver_fp_format);
	fout.write((char*)(&temp),byte_per_ver_coord);
	temp = floatToFixed( boundingSphere.center.z,ver_fp_format);
	fout.write((char*)(&temp),byte_per_ver_coord);
	temp = floatToFixed( boundingSphere.radius,ver_fp_format);
	fout.write((char*)(&temp),byte_per_ver_coord);

	cout<<"\nFinish write output file..";
	fout.close();
}